package com.juitar.base.fingering;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.juitar.base.Chord;
import com.juitar.base.Fingering;
import com.juitar.base.Tuning;

public class RecursiveFingeringBuilder implements IFingeringBuilder {

    private static final int MAX_FRETS = 3;

    public Collection<Fingering> createFingerings(Chord chord, Tuning tuning) {
        HashSet<Fingering> set = new HashSet<Fingering>(256);
        int fing[] = new int[tuning.strings()];
        for (int i = 1; i < 12; i++) {
            createFingerings(i, tuning.strings() - 1, fing, chord, tuning, set);
        }
        List<Fingering> result = new LinkedList<Fingering>(set);
        Collections.sort(result, fingeringComparator);
        return result;
    }

    private void createFingerings(int fret, int string, int[] fing, Chord chord, Tuning tuning,
            Set<Fingering> result) {
        if (string < 0) {
            int[] clone = fing.clone();
            Fingering f = new Fingering(clone, chord, tuning);
            if (f.isChord()) {
                result.add(f);
            }
            return;
        }

        if (string >= 4) {
            // Mute bass string
            fing[string] = -1;
            createFingerings(fret, string - 1, fing, chord, tuning, result);
        }

        int open = tuning.getOpenStringNotes()[string];
        boolean found = false;
        if (chord.contains(open) > 0) {
            fing[string] = 0;
            createFingerings(fret, string - 1, fing, chord, tuning, result);
            found = true;
        }
        for (int i = 0; i < MAX_FRETS; i++) {
            int note = open + i + fret;
            if (chord.contains(note) > 0) {
                found = true;
                fing[string] = fret + i;
                createFingerings(fret, string - 1, fing, chord, tuning, result);
            }
        }
        if (!found) {
            fing[string] = -1;
            createFingerings(fret, string - 1, fing, chord, tuning, result);
        }
    }

    private static FingeringComparator fingeringComparator = new FingeringComparator();

}
